The goal of this analysis is to estimate the latency and the capacity of two connections. To do it, two datasets of ping logs are available. One from a short on-campus connection :

http://mescal.imag.fr/membres/arnaud.legrand/teaching/2014/RICM4_EP_ping/liglab2.log.gz

and another from a connection to a remote web site that is popular, and therefore has a heavy load :

http://mescal.imag.fr/membres/arnaud.legrand/teaching/2014/RICM4_EP_ping/stackoverflow.log.gz.

The initial data is formatted like below :

readLines('data/liglab2.log', n=2);
[1] "[1421761682.052172] 665 bytes from lig-publig.imag.fr (129.88.11.7): icmp_seq=1 ttl=60 time=22.5 ms" 
[2] "[1421761682.277315] 1373 bytes from lig-publig.imag.fr (129.88.11.7): icmp_seq=1 ttl=60 time=21.2 ms"

prepare_data.sh formats the logs in csv and removes incorrect lines.


To format logs, execute prepare_data.sh script. Formatted files will be saved in the data/ directory.

WARNING : liglab2.log and stackoverflow.log must be placed in data/ directory.


First dataset

The first dataset is called liglab2.csv, and has been converted from the liblab2.log.

Data verification

Reading the data

data <- read.csv(file="./data/liglab2.csv", header=TRUE, sep=" ");

Checking that header and first data seems corrects.

# Header
colnames(data)
[1] "date" "size" "time"
#First rows of the dataset
head(data)

We can convert the date timestamp to a more readable format, and check if the data is in a correct format.

data$date = anytime(data$date);

# Check if date has been successfully converted in POSIX dates
class(data$date);
[1] "POSIXct" "POSIXt" 
#Size should be integer
class(data$size);
[1] "integer"
#time should be at least numeric
class(data$time);
[1] "numeric"

Are there any missing data?

na_records = apply(data, 1, function (x) any(is.na(x)))
data[na_records,]

Analysis

Plot the time variation versus date could reveal patterns. Plotly allows to zoom dynamically in the data, which is convenient to explore data.

p <- plot_ly(data, x = ~date, mode='lines')%>%
add_trace(y = ~time, name = "date", type="scatter", mode = 'lines');
p

But in this case, there is not any easily predictible pattern.

There may be also correlation between the size of a packet and its size. But a value in [-0.2, 0.2] doesn’t prove any link between two parameters.

cor(subset(data, select=c("size", "time")));
          size      time
size 1.0000000 0.1802414
time 0.1802414 1.0000000

There is no evident correlation between time of response and packet size. Let’s plot the data to have a more general point of view.

ggplot(data = data, mapping = aes(x =size, y = time))+
  geom_point(size=1)+ ggtitle("Ping time according to packet size")

Thanks to this plot, two classes can be easily seen. The packets which have a size below about 1450-1500 bytes, and the others. To be more precise, there is a zoom in the specified zone.

ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(size=1)+
coord_cartesian(xlim=c(1450,1500))+ ggtitle("Ping time with a zoom on packet size of ~ 1480 bytes")

According to this set of data, the size delimiter between the two classes would be 1481. The difference between the two classes time could be explained by fragmentation. When a packet reaches a limit size (MTU), it is fragmented in smaller ones. The receiver also has to wait every packets, merge them and respond to the ping, which costs time.

Nevertheless, this way of showing the data can be a bit confusing, because overlapping points and a single point have the same appearance. Sometimes, the geom_count option could solve this problem, but it is not really our case :

ggplot(data = data, mapping = aes(x =size, y = time))+
geom_count() + ggtitle("Ping time according to packet size")

Points are too close from each other. A bit of transparency could be better. To make it more readable, let’s plot both classes separately with a zoom on lower bound.

# Packets whose size is < 1481
low_size = subset(data, size < 1481);

# Packets whose size is >= 1481
high_size = subset(data, size >= 1481);

# Zooming in lower bounds.
p_low <- ggplot(data = low_size, mapping = aes(x =size,y = time)) +
              geom_point(alpha= 0.1)+ggtitle("Ping delays of packets \nwhose size < 1481") + ylim(0,100)
p_high <- ggplot(data = high_size, mapping = aes(x =size, y = time)) +
              geom_point(alpha= 0.1)+ ggtitle("Ping delays of packets \nwhose size >= 1481")
grid.arrange(p_low, p_high, nrow = 1)

It is way more effective. Thanks to this representation, we can see that a most of times are near zero. This is validated by basic stastistics with the sumarry command.

summary(low_size$time);
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.240   1.350   3.517   1.490 276.000 
summary(high_size$time);
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.030   2.250   2.320   9.789   2.450 187.000 

Histogram are also useful to have an idea of the times repartition. Despite very high maximum, the 3rd quartile values show that most of times are extremely low (~2ms). Therefore, a thin binwidth is needed.

p_low <- ggplot(data = low_size, mapping = aes(x =time)) +
              geom_histogram(binwidth=2) + ggtitle("Time frequency of packet \nwhose size < 1481")
p_high <- ggplot(data = high_size, mapping = aes(x =time)) +
              geom_histogram(binwidth=2)+ggtitle("Time frequency of packet \nwhose size >= 1481")
grid.arrange(p_low, p_high, nrow = 1)

Most of the times are about 0~2 ms.

Let’s use linear regression to determine the latency (L), and the capacity (C), with the formula \(T(S) = L + \frac{1}{C} * S\). The linear regression allows to predict y when only x is known, with the following equation \(y = \beta_{1} + \beta_{2}x\), where \(\beta_{1}\) is the intercept, and \(\beta_{2}\) is the slope.

In the current case, \(\beta_{1}\) would be \(L\), and \(\beta_{2}\) would be \(\frac{1}{C}\)

p <- ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.1) + geom_smooth(data=subset(data, size >= 1481), method="lm", size=1.5, aes(colour="Second class linear reg"))+ ylim(0,100) +
geom_smooth(data=subset(data, size < 1481), method="lm", size=1.5, aes(colour="First class linear reg")) + ggtitle("Ping time according to packet size with linear regressions")
p

For the first class of data, the low value of R squared does not means that the model is bad. In our case, it could significate that there is a high variability, which is visible in the previous plot. Standard error are low, but it seams that the intercept value is a bit too high to be realistic. The coefficient is approximately 3.2, which is greater than the 3rd quartile of the dataset. This is caused by the small number of long responses, which have a huge impact on the regression.

# Linear regression of packet size < 1481
linear_reg <- lm(time ~ size, # regression formula
                data=low_size) # data set
summary(linear_reg);

Call:
lm(formula = time ~ size, data = low_size)

Residuals:
    Min      1Q  Median      3Q     Max 
 -2.475  -2.247  -2.189  -2.088 272.489 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 3.276e+00  7.231e-02  45.301  < 2e-16 ***
size        3.263e-04  8.497e-05   3.841 0.000123 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.432 on 32665 degrees of freedom
Multiple R-squared:  0.0004514, Adjusted R-squared:  0.0004208 
F-statistic: 14.75 on 1 and 32665 DF,  p-value: 0.000123

For the second class, the linear regression is totally inappropriate, the slope, the standard error and R-squared are bad.


# Linear regression of packet size >= 1481
linear_reg <- lm(time ~ size, # regression formula
                data=high_size) # data set
summary(linear_reg);

Call:
lm(formula = time ~ size, data = high_size)

Residuals:
    Min      1Q  Median      3Q     Max 
 -8.276  -7.729  -7.354  -6.999 177.215 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept) 5.289833   2.244269   2.357   0.0184 *
size        0.002579   0.001281   2.012   0.0442 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 20.74 on 11367 degrees of freedom
Multiple R-squared:  0.0003562, Adjusted R-squared:  0.0002682 
F-statistic:  4.05 on 1 and 11367 DF,  p-value: 0.0442

A quantile regression should be more accurate. Testing different taus could help deciding which percentage of times to take (https://data.library.virginia.edu/getting-started-with-quantile-regression/) Each black dot is the slope coefficient for the quantile indicated on the x axis. The red lines are the least squares estimate and its confidence interval. Uper quantile is well beyond the least squares. A tau of 0.8 could be suitable, and ensures the previous results.

rqfit <- rq(time ~ size, data = low_size, tau=1:9/10)
plot(summary(rqfit), parm="size", main="Size coefficient along with confidence intervals for each tau (Low class)")


rqfit <- rq(time ~ size, data = high_size, tau=1:9/10)
plot(summary(rqfit), parm="size", main="Size coefficient along with confidence intervals for each tau (High class)")

Visually, this quantile regression seems way better than the linear one.

p <- ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.2) +
geom_quantile(quantiles = 0.8, data=low_size, colour = "blue", size = 2)+
geom_quantile(quantiles = 0.8, data=high_size, colour = "red", size = 2) + ylim(0,100) + ggtitle("Ping time according to packet size with quantile regressions")
p

The extremely low p-value and standard error seems to confirm the graphical result.

# Low class quantile regression
summary(lm(time ~ size, data=low_size[low_size$time < quantile(low_size$time, 0.8),]));

Call:
lm(formula = time ~ size, data = low_size[low_size$time < quantile(low_size$time, 
    0.8), ])

Residuals:
     Min       1Q   Median       3Q      Max 
-0.21996 -0.04801 -0.00938  0.03693  0.38633 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.145e+00  8.740e-04  1310.0   <2e-16 ***
size        2.235e-04  1.051e-06   212.7   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.07029 on 26081 degrees of freedom
Multiple R-squared:  0.6343,    Adjusted R-squared:  0.6343 
F-statistic: 4.523e+04 on 1 and 26081 DF,  p-value: < 2.2e-16
# High class quantile regression
summary(lm(time ~ size, data=high_size[high_size$time < quantile(high_size$time, 0.8),]));

Call:
lm(formula = time ~ size, data = high_size[high_size$time < quantile(high_size$time, 
    0.8), ])

Residuals:
      Min        1Q    Median        3Q       Max 
-0.227738 -0.059902 -0.005723  0.049949  0.296367 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.904e+00  1.005e-02  189.38   <2e-16 ***
size        2.239e-04  5.748e-06   38.95   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.08342 on 9089 degrees of freedom
Multiple R-squared:  0.143, Adjusted R-squared:  0.1429 
F-statistic:  1517 on 1 and 9089 DF,  p-value: < 2.2e-16

According to the intercept and size estimation, for the first class : the latency \(L\) is approximately equal to \(1.145 * 10^-3\) seconds, and the capacity \(C\) to \(\frac{1}{0.0002235} \simeq 4474\) bytes/s.

For the second class, \(L \simeq 1.9 * 10^-3\) seconds and \(C \simeq \frac{1}{0.0002239} = \simeq 4466\) bytes/s. The capacity doesn’t really change, which is pretty logical, and the latency quite increases with the fragmentation of the package.

Second dataset

The second dataset is called stackoverflow.csv, and has been converted from the stackoverflow.log. It results from a connection to a remote web site connection with heavy loads.

Data verification

The first steps are similar to the first dataset. Thus, they are not detailed.

data <- read.csv(file="./data/stackoverflow.csv", header=TRUE, sep=" ");

# Header
colnames(data)
[1] "date" "size" "time"
# First rows 
head(data)
data$date = anytime(data$date);

# Check if date has been successfully converted in POSIX dates
class(data$date);
[1] "POSIXct" "POSIXt" 
#Size should be integer
class(data$size);
[1] "integer"
#time should be at least numeric
class(data$time);
[1] "integer"
na_records = apply(data, 1, function (x) any(is.na(x)))
data[na_records,]

Analysis

Thanks to this plot, we can see that the gap between extreme values is less important. Like the first set, a predictible pattern could be a bit tough to detect.

p <- plot_ly(data, x = ~date, mode='lines')%>%
add_trace(y = ~time, name = "date", type="scatter", mode = 'lines');
p

The server being far away, the minimum time value for a ping is bigger.

summary(data[["time"]]);
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  110.0   111.0   111.0   114.2   112.0   163.0 

There is even less correlation between size and times.

cor(subset(data, select=c("size", "time")));
      size  time
size 1.000 0.152
time 0.152 1.000

Let’s plot the data to have a more general point of view.

ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.1) + ggtitle("Ping times according to packet size")

Without any surprise, there is the same average delimitation between packets whose size is below or greater than 1482 bytes. The protocol used is the same, whatever the distance between clients and the web server.

ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.3)+
coord_cartesian(xlim=c(1450,1500)) + ggtitle("Ping time with a zoom on packet size of ~ 1480 bytes")

Like the short connection, most times are gathered at lower bounds. Nevertheless, we can see with histograms that outliers are more equally distributed than in the first dataset. In addition to fragmentation, waiting time on the server could be increased by heavy load, creating requests queues. If the initial ICMP packet is divided into multiple ones (fragmentation), then packets could arrive at different time, and be far from each other in this queue, which could explain the time difference between small and big packets.

# Packets whose size is < 1482
low_size = subset(data, size < 1482);

# Packets whose size is >= 1482
high_size = subset(data, size >= 1482);

p_low <- ggplot(data = low_size, mapping = aes(x =time)) +
              geom_histogram(binwidth=2) + ggtitle("Time frequency of packet \nwhose size < 1482")
p_high <- ggplot(data = high_size, mapping = aes(x =time)) +
              geom_histogram(binwidth=2)+ggtitle("Time frequency of packet \nwhose size >= 1482")
grid.arrange(p_low, p_high, nrow = 1)

The linear regression seems also inappropriate, including the small number of higher times. The explanations are the same as the first dataset ones.

p <- ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.3) + geom_smooth(data=subset(data, size >= 1480), method="lm", size=1.5, aes(colour="Second class linear reg"))+
geom_smooth(data=subset(data, size < 1480), method="lm", size=1.5, aes(colour="First class linear reg"))+ ggtitle("Ping time according to packet size with linear regressions")
p

# Linear regression of packet size < 1482
linear_reg <- lm(time ~ size, # regression formula
                data=low_size) # data set
summary(linear_reg);

Call:
lm(formula = time ~ size, data = low_size)

Residuals:
   Min     1Q Median     3Q    Max 
-3.264 -2.277 -2.255 -2.231 36.767 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.132e+02  1.653e-01 684.992   <2e-16 ***
size        4.785e-05  1.934e-04   0.247    0.805    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.815 on 5019 degrees of freedom
Multiple R-squared:  1.22e-05,  Adjusted R-squared:  -0.000187 
F-statistic: 0.06124 on 1 and 5019 DF,  p-value: 0.8046
# Linear regression of packet size >= 1482
linear_reg <- lm(time ~ size, # regression formula
                data=high_size) # data set
summary(linear_reg);

Call:
lm(formula = time ~ size, data = high_size)

Residuals:
   Min     1Q Median     3Q    Max 
-6.427 -5.136 -4.791 -4.447 45.683 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 120.294063   3.210137  37.473   <2e-16 ***
size         -0.001934   0.001838  -1.052    0.293    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 11.9 on 1801 degrees of freedom
Multiple R-squared:  0.0006144, Adjusted R-squared:  5.953e-05 
F-statistic: 1.107 on 1 and 1801 DF,  p-value: 0.2928

Both visually and numerically with p-value and r-squared, this linear regression seems inappropriate.

A quantile regression should be more accurate. The range between the 3rd quartile and the minimum value of time is the same for packets whose size is below 1482, and only one for the others. Then, a 3rd quartile regression should be perfect to approximate latency and capacity.

#
summary(low_size[["time"]]);
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  110.0   111.0   111.0   113.3   111.0   150.0 
summary(high_size[["time"]]);
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  111.0   112.0   112.0   116.9   112.0   163.0 
p <- ggplot(data = data, mapping = aes(x =size, y = time))+
geom_point(alpha=0.2) + geom_quantile(quantiles = 0.75, data=high_size, colour = "red", size = 2) +
geom_quantile(quantiles = 0.75, data=low_size, colour = "blue", size = 2) + ggtitle("Ping time according to packet size with quantile regressions")
p

Results seem pretty good. The size coefficients in both quantiles regression below don’t allow to reject the null hypothesis. It is hard to compute the capacity C because of this uncertainty. The (Intercept) coefficient has an extremely low standard error, and is estimated at 110 ms.

# Low class 3r quartile regression
summary(lm(time ~ size, data=low_size[low_size$time < quantile(low_size$time, 0.75),]));

Call:
lm(formula = time ~ size, data = low_size[low_size$time < quantile(low_size$time, 
    0.75), ])

Residuals:
       Min         1Q     Median         3Q        Max 
-3.996e-12  1.580e-14  1.640e-14  1.690e-14  1.740e-14 

Coefficients:
             Estimate Std. Error   t value Pr(>|t|)    
(Intercept) 1.100e+02  3.154e-14 3.487e+15   <2e-16 ***
size        4.002e-18  9.151e-17 4.400e-02    0.965    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.563e-13 on 244 degrees of freedom
Multiple R-squared:  0.4995,    Adjusted R-squared:  0.4974 
F-statistic: 243.5 on 1 and 244 DF,  p-value: < 2.2e-16
# High class 3rd quartile regression
summary(lm(time ~ size, data=high_size[high_size$time < quantile(high_size$time, 0.75),]));

Call:
lm(formula = time ~ size, data = high_size[high_size$time < quantile(high_size$time, 
    0.75), ])

Residuals:
       Min         1Q     Median         3Q        Max 
-3.460e-14 -2.884e-14 -1.731e-14 -1.940e-15  1.046e-12 

Coefficients:
              Estimate Std. Error    t value Pr(>|t|)    
(Intercept)  1.110e+02  2.487e-13  4.464e+14   <2e-16 ***
size        -1.601e-16  1.548e-16 -1.034e+00    0.305    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.278e-13 on 69 degrees of freedom
Multiple R-squared:  0.5021,    Adjusted R-squared:  0.4949 
F-statistic: 69.58 on 1 and 69 DF,  p-value: 4.751e-12

The second dataset shows long distance latencies. In contrary to the first dataset, the latencies are the same for the two classes (~110 ms), because the time involved by the data travel is far superior to the packets fragmentation cost. Moreover, the fact that size doesn’t influence at all ping time for the three first quartiles makes the analysis of the capacity not relevant.in this scenario.

Conclusion

The most important point of this analysis to remember, could be that displaying only summaries or simple linear regression isn’t sufficient. The first dataset’s linear regression had great coefficients statistics. It is only after displaying the data that a suitable analysis could be done.

Latencies have been evaluated for each dataset. Nevertheless, the capacity could not be evaluated on long distance connection.

LS0tCnRpdGxlOiAiU3ViamVjdCA0OiBMYXRlbmN5IGFuZCBjYXBhY2l0eSBlc3RpbWF0aW9uIGZvciBhIG5ldHdvcmsgY29ubmVjdGlvbiBmcm9tIGFzeW1tZXRyaWMgbWVhc3VyZW1lbnRzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpUaGUgZ29hbCBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGVzdGltYXRlIHRoZSBsYXRlbmN5IGFuZCB0aGUgY2FwYWNpdHkgb2YgdHdvIGNvbm5lY3Rpb25zLiBUbyBkbyBpdCwgdHdvIGRhdGFzZXRzIG9mIHBpbmcgbG9ncyBhcmUgYXZhaWxhYmxlLiBPbmUgZnJvbSBhIHNob3J0IG9uLWNhbXB1cyBjb25uZWN0aW9uIDogCgpodHRwOi8vbWVzY2FsLmltYWcuZnIvbWVtYnJlcy9hcm5hdWQubGVncmFuZC90ZWFjaGluZy8yMDE0L1JJQ000X0VQX3BpbmcvbGlnbGFiMi5sb2cuZ3oKCmFuZCBhbm90aGVyIGZyb20gYSBjb25uZWN0aW9uIHRvIGEgcmVtb3RlIHdlYiBzaXRlIHRoYXQgaXMgcG9wdWxhciwgYW5kIHRoZXJlZm9yZSBoYXMgYSBoZWF2eSBsb2FkIDoKCmh0dHA6Ly9tZXNjYWwuaW1hZy5mci9tZW1icmVzL2FybmF1ZC5sZWdyYW5kL3RlYWNoaW5nLzIwMTQvUklDTTRfRVBfcGluZy9zdGFja292ZXJmbG93LmxvZy5nei4KClRoZSBpbml0aWFsIGRhdGEgaXMgZm9ybWF0dGVkIGxpa2UgYmVsb3cgOiAKCmBgYHtyfQpyZWFkTGluZXMoJ2RhdGEvbGlnbGFiMi5sb2cnLCBuPTIpOwpgYGAKCipwcmVwYXJlX2RhdGEuc2gqIGZvcm1hdHMgdGhlIGxvZ3MgaW4gY3N2IGFuZCByZW1vdmVzIGluY29ycmVjdCBsaW5lcy4KCioqKiAKVG8gZm9ybWF0IGxvZ3MsIGV4ZWN1dGUgKnByZXBhcmVfZGF0YS5zaCogc2NyaXB0LiBGb3JtYXR0ZWQgZmlsZXMgd2lsbCBiZSBzYXZlZCBpbiB0aGUgKmRhdGEvKiBkaXJlY3RvcnkuCgoqKldBUk5JTkcgOioqICpsaWdsYWIyLmxvZyogYW5kICpzdGFja292ZXJmbG93LmxvZyogbXVzdCBiZSBwbGFjZWQgaW4gKmRhdGEvKiBkaXJlY3RvcnkuCgoqKioKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQppZighcmVxdWlyZSgiYW55dGltZSIpKSBpbnN0YWxsLnBhY2thZ2VzKCJhbnl0aW1lIik7bGlicmFyeSgiYW55dGltZSIpOwppZighcmVxdWlyZSgicGxvdGx5IikpaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5Iik7bGlicmFyeSgicGxvdGx5Iik7CmlmKCFyZXF1aXJlKCJncmlkRXh0cmEiKSlpbnN0YWxsLnBhY2thZ2VzKCJncmlkRXh0cmEiKTtsaWJyYXJ5KCJncmlkRXh0cmEiKTsKaWYoIXJlcXVpcmUoInF1YW50cmVnIikpaW5zdGFsbC5wYWNrYWdlcygicXVhbnRyZWciKTtsaWJyYXJ5KCJxdWFudHJlZyIpOwpgYGAKCiMjIEZpcnN0IGRhdGFzZXQKClRoZSBmaXJzdCBkYXRhc2V0IGlzIGNhbGxlZCBsaWdsYWIyLmNzdiwgYW5kIGhhcyBiZWVuIGNvbnZlcnRlZCBmcm9tIHRoZSBsaWJsYWIyLmxvZy4gCgojIyMgRGF0YSB2ZXJpZmljYXRpb24KClJlYWRpbmcgdGhlIGRhdGEKYGBge3J9CmRhdGEgPC0gcmVhZC5jc3YoZmlsZT0iLi9kYXRhL2xpZ2xhYjIuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iICIpOwpgYGAKIApDaGVja2luZyB0aGF0IGhlYWRlciBhbmQgZmlyc3QgZGF0YSBzZWVtcyBjb3JyZWN0cy4KYGBge3J9CiMgSGVhZGVyCmNvbG5hbWVzKGRhdGEpCgojRmlyc3Qgcm93cyBvZiB0aGUgZGF0YXNldApoZWFkKGRhdGEpCmBgYAoKCldlIGNhbiBjb252ZXJ0IHRoZSBkYXRlIHRpbWVzdGFtcCB0byBhIG1vcmUgcmVhZGFibGUgZm9ybWF0LCBhbmQgY2hlY2sgaWYgdGhlIGRhdGEgaXMgaW4gYSBjb3JyZWN0IGZvcm1hdC4KYGBge3J9CmRhdGEkZGF0ZSA9IGFueXRpbWUoZGF0YSRkYXRlKTsKCiMgQ2hlY2sgaWYgZGF0ZSBoYXMgYmVlbiBzdWNjZXNzZnVsbHkgY29udmVydGVkIGluIFBPU0lYIGRhdGVzCmNsYXNzKGRhdGEkZGF0ZSk7CgojU2l6ZSBzaG91bGQgYmUgaW50ZWdlcgpjbGFzcyhkYXRhJHNpemUpOwoKI3RpbWUgc2hvdWxkIGJlIGF0IGxlYXN0IG51bWVyaWMKY2xhc3MoZGF0YSR0aW1lKTsKYGBgCgpBcmUgdGhlcmUgYW55IG1pc3NpbmcgZGF0YT8KYGBge3J9Cm5hX3JlY29yZHMgPSBhcHBseShkYXRhLCAxLCBmdW5jdGlvbiAoeCkgYW55KGlzLm5hKHgpKSkKZGF0YVtuYV9yZWNvcmRzLF0KYGBgCgojIyMgQW5hbHlzaXMKClBsb3QgdGhlIHRpbWUgdmFyaWF0aW9uIHZlcnN1cyBkYXRlIGNvdWxkIHJldmVhbCBwYXR0ZXJucy4gUGxvdGx5IGFsbG93cyB0byB6b29tIGR5bmFtaWNhbGx5IGluIHRoZSBkYXRhLCB3aGljaCBpcyBjb252ZW5pZW50IHRvIGV4cGxvcmUgZGF0YS4KYGBge3J9CnAgPC0gcGxvdF9seShkYXRhLCB4ID0gfmRhdGUsIG1vZGU9J2xpbmVzJyklPiUKYWRkX3RyYWNlKHkgPSB+dGltZSwgbmFtZSA9ICJkYXRlIiwgdHlwZT0ic2NhdHRlciIsIG1vZGUgPSAnbGluZXMnKTsKcApgYGAKQnV0IGluIHRoaXMgY2FzZSwgdGhlcmUgaXMgbm90IGFueSBlYXNpbHkgcHJlZGljdGlibGUgcGF0dGVybi4KClRoZXJlIG1heSBiZSBhbHNvIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHNpemUgb2YgYSBwYWNrZXQgYW5kIGl0cyBzaXplLiBCdXQgYSB2YWx1ZSBpbiBbLTAuMiwgMC4yXSBkb2Vzbid0IHByb3ZlIGFueSBsaW5rIGJldHdlZW4gdHdvIHBhcmFtZXRlcnMuCmBgYHtyfQpjb3Ioc3Vic2V0KGRhdGEsIHNlbGVjdD1jKCJzaXplIiwgInRpbWUiKSkpOwpgYGAKVGhlcmUgaXMgbm8gZXZpZGVudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRpbWUgb2YgcmVzcG9uc2UgYW5kIHBhY2tldCBzaXplLiBMZXQncyBwbG90IHRoZSBkYXRhIHRvIGhhdmUgYSBtb3JlIGdlbmVyYWwgcG9pbnQgb2Ygdmlldy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBhZXMoeCA9c2l6ZSwgeSA9IHRpbWUpKSsKICBnZW9tX3BvaW50KHNpemU9MSkrIGdndGl0bGUoIlBpbmcgdGltZSBhY2NvcmRpbmcgdG8gcGFja2V0IHNpemUiKQpgYGAKClRoYW5rcyB0byB0aGlzIHBsb3QsIHR3byBjbGFzc2VzIGNhbiBiZSBlYXNpbHkgc2Vlbi4gVGhlIHBhY2tldHMgd2hpY2ggaGF2ZSBhIHNpemUgYmVsb3cgYWJvdXQgMTQ1MC0xNTAwIGJ5dGVzLCBhbmQgdGhlIG90aGVycy4gVG8gYmUgbW9yZSBwcmVjaXNlLCB0aGVyZSBpcyBhIHpvb20gaW4gdGhlIHNwZWNpZmllZCB6b25lLgoKYGBge3J9CmdncGxvdChkYXRhID0gZGF0YSwgbWFwcGluZyA9IGFlcyh4ID1zaXplLCB5ID0gdGltZSkpKwpnZW9tX3BvaW50KHNpemU9MSkrCmNvb3JkX2NhcnRlc2lhbih4bGltPWMoMTQ1MCwxNTAwKSkrIGdndGl0bGUoIlBpbmcgdGltZSB3aXRoIGEgem9vbSBvbiBwYWNrZXQgc2l6ZSBvZiB+IDE0ODAgYnl0ZXMiKQpgYGAKQWNjb3JkaW5nIHRvIHRoaXMgc2V0IG9mIGRhdGEsIHRoZSBzaXplIGRlbGltaXRlciBiZXR3ZWVuIHRoZSB0d28gY2xhc3NlcyB3b3VsZCBiZSAxNDgxLiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gY2xhc3NlcyB0aW1lIGNvdWxkIGJlIGV4cGxhaW5lZCBieSBmcmFnbWVudGF0aW9uLiBXaGVuIGEgcGFja2V0IHJlYWNoZXMgYSBsaW1pdCBzaXplIChNVFUpLCBpdCBpcyBmcmFnbWVudGVkIGluIHNtYWxsZXIgb25lcy4gVGhlIHJlY2VpdmVyIGFsc28gaGFzIHRvIHdhaXQgZXZlcnkgcGFja2V0cywgbWVyZ2UgdGhlbSBhbmQgcmVzcG9uZCB0byB0aGUgcGluZywgd2hpY2ggY29zdHMgdGltZS4gCgpOZXZlcnRoZWxlc3MsIHRoaXMgd2F5IG9mIHNob3dpbmcgdGhlIGRhdGEgY2FuIGJlIGEgYml0IGNvbmZ1c2luZywgYmVjYXVzZSBvdmVybGFwcGluZyBwb2ludHMgYW5kIGEgc2luZ2xlIHBvaW50IGhhdmUgdGhlIHNhbWUgYXBwZWFyYW5jZS4gU29tZXRpbWVzLCB0aGUgZ2VvbV9jb3VudCBvcHRpb24gY291bGQgc29sdmUgdGhpcyBwcm9ibGVtLCBidXQgaXQgaXMgbm90IHJlYWxseSBvdXIgY2FzZSA6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fY291bnQoKSArIGdndGl0bGUoIlBpbmcgdGltZSBhY2NvcmRpbmcgdG8gcGFja2V0IHNpemUiKQpgYGAKUG9pbnRzIGFyZSB0b28gY2xvc2UgZnJvbSBlYWNoIG90aGVyLiBBIGJpdCBvZiB0cmFuc3BhcmVuY3kgY291bGQgYmUgYmV0dGVyLiBUbyBtYWtlIGl0IG1vcmUgcmVhZGFibGUsIGxldCdzIHBsb3QgYm90aCBjbGFzc2VzIHNlcGFyYXRlbHkgd2l0aCBhIHpvb20gb24gbG93ZXIgYm91bmQuCgpgYGB7cn0KIyBQYWNrZXRzIHdob3NlIHNpemUgaXMgPCAxNDgxCmxvd19zaXplID0gc3Vic2V0KGRhdGEsIHNpemUgPCAxNDgxKTsKCiMgUGFja2V0cyB3aG9zZSBzaXplIGlzID49IDE0ODEKaGlnaF9zaXplID0gc3Vic2V0KGRhdGEsIHNpemUgPj0gMTQ4MSk7CgojIFpvb21pbmcgaW4gbG93ZXIgYm91bmRzLgpwX2xvdyA8LSBnZ3Bsb3QoZGF0YSA9IGxvd19zaXplLCBtYXBwaW5nID0gYWVzKHggPXNpemUseSA9IHRpbWUpKSArCiAgICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYT0gMC4xKStnZ3RpdGxlKCJQaW5nIGRlbGF5cyBvZiBwYWNrZXRzIFxud2hvc2Ugc2l6ZSA8IDE0ODEiKSArIHlsaW0oMCwxMDApCnBfaGlnaCA8LSBnZ3Bsb3QoZGF0YSA9IGhpZ2hfc2l6ZSwgbWFwcGluZyA9IGFlcyh4ID1zaXplLCB5ID0gdGltZSkpICsKICAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhPSAwLjEpKyBnZ3RpdGxlKCJQaW5nIGRlbGF5cyBvZiBwYWNrZXRzIFxud2hvc2Ugc2l6ZSA+PSAxNDgxIikKZ3JpZC5hcnJhbmdlKHBfbG93LCBwX2hpZ2gsIG5yb3cgPSAxKQpgYGAKSXQgaXMgd2F5IG1vcmUgZWZmZWN0aXZlLiBUaGFua3MgdG8gdGhpcyByZXByZXNlbnRhdGlvbiwgd2UgY2FuIHNlZSB0aGF0IGEgbW9zdCBvZiB0aW1lcyBhcmUgbmVhciB6ZXJvLiBUaGlzIGlzIHZhbGlkYXRlZCBieSBiYXNpYyBzdGFzdGlzdGljcyB3aXRoIHRoZSAqc3VtYXJyeSogY29tbWFuZC4KYGBge3J9CnN1bW1hcnkobG93X3NpemUkdGltZSk7CgpzdW1tYXJ5KGhpZ2hfc2l6ZSR0aW1lKTsKYGBgCkhpc3RvZ3JhbSBhcmUgYWxzbyB1c2VmdWwgdG8gaGF2ZSBhbiBpZGVhIG9mIHRoZSB0aW1lcyByZXBhcnRpdGlvbi4gRGVzcGl0ZSB2ZXJ5IGhpZ2ggbWF4aW11bSwgdGhlIDNyZCBxdWFydGlsZSB2YWx1ZXMgc2hvdyB0aGF0IG1vc3Qgb2YgdGltZXMgYXJlIGV4dHJlbWVseSBsb3cgKH4ybXMpLiBUaGVyZWZvcmUsIGEgdGhpbiBiaW53aWR0aCBpcyBuZWVkZWQuCgpgYGB7cn0KcF9sb3cgPC0gZ2dwbG90KGRhdGEgPSBsb3dfc2l6ZSwgbWFwcGluZyA9IGFlcyh4ID10aW1lKSkgKwogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTIpICsgZ2d0aXRsZSgiVGltZSBmcmVxdWVuY3kgb2YgcGFja2V0IFxud2hvc2Ugc2l6ZSA8IDE0ODEiKQpwX2hpZ2ggPC0gZ2dwbG90KGRhdGEgPSBoaWdoX3NpemUsIG1hcHBpbmcgPSBhZXMoeCA9dGltZSkpICsKICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0yKStnZ3RpdGxlKCJUaW1lIGZyZXF1ZW5jeSBvZiBwYWNrZXQgXG53aG9zZSBzaXplID49IDE0ODEiKQpncmlkLmFycmFuZ2UocF9sb3csIHBfaGlnaCwgbnJvdyA9IDEpCmBgYApNb3N0IG9mIHRoZSB0aW1lcyBhcmUgYWJvdXQgMH4yIG1zLiAKCkxldCdzIHVzZSBsaW5lYXIgcmVncmVzc2lvbiB0byBkZXRlcm1pbmUgdGhlIGxhdGVuY3kgKEwpLCBhbmQgdGhlIGNhcGFjaXR5IChDKSwgd2l0aCB0aGUgZm9ybXVsYSAkVChTKSA9IEwgKyBcZnJhY3sxfXtDfSAqIFMkLiBUaGUgbGluZWFyIHJlZ3Jlc3Npb24gYWxsb3dzIHRvIHByZWRpY3QgeSB3aGVuIG9ubHkgeCBpcyBrbm93biwgd2l0aCB0aGUgZm9sbG93aW5nIGVxdWF0aW9uICR5ID0gXGJldGFfezF9ICsgXGJldGFfezJ9eCQsIHdoZXJlICRcYmV0YV97MX0kIGlzIHRoZSBpbnRlcmNlcHQsIGFuZCAkXGJldGFfezJ9JCBpcyB0aGUgc2xvcGUuIAoKSW4gdGhlIGN1cnJlbnQgY2FzZSwgJFxiZXRhX3sxfSQgd291bGQgYmUgJEwkLCBhbmQgJFxiZXRhX3syfSQgd291bGQgYmUgJFxmcmFjezF9e0N9JAoKYGBge3J9CnAgPC0gZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fcG9pbnQoYWxwaGE9MC4xKSArIGdlb21fc21vb3RoKGRhdGE9c3Vic2V0KGRhdGEsIHNpemUgPj0gMTQ4MSksIG1ldGhvZD0ibG0iLCBzaXplPTEuNSwgYWVzKGNvbG91cj0iU2Vjb25kIGNsYXNzIGxpbmVhciByZWciKSkrIHlsaW0oMCwxMDApICsKZ2VvbV9zbW9vdGgoZGF0YT1zdWJzZXQoZGF0YSwgc2l6ZSA8IDE0ODEpLCBtZXRob2Q9ImxtIiwgc2l6ZT0xLjUsIGFlcyhjb2xvdXI9IkZpcnN0IGNsYXNzIGxpbmVhciByZWciKSkgKyBnZ3RpdGxlKCJQaW5nIHRpbWUgYWNjb3JkaW5nIHRvIHBhY2tldCBzaXplIHdpdGggbGluZWFyIHJlZ3Jlc3Npb25zIikKcApgYGAKCkZvciB0aGUgZmlyc3QgY2xhc3Mgb2YgZGF0YSwgdGhlIGxvdyB2YWx1ZSBvZiBSIHNxdWFyZWQgZG9lcyBub3QgbWVhbnMgdGhhdCB0aGUgbW9kZWwgaXMgYmFkLiBJbiBvdXIgY2FzZSwgaXQgY291bGQgc2lnbmlmaWNhdGUgdGhhdCB0aGVyZSBpcyBhIGhpZ2ggdmFyaWFiaWxpdHksIHdoaWNoIGlzIHZpc2libGUgaW4gdGhlIHByZXZpb3VzIHBsb3QuIFN0YW5kYXJkIGVycm9yIGFyZSBsb3csIGJ1dCBpdCBzZWFtcyB0aGF0IHRoZSBpbnRlcmNlcHQgdmFsdWUgaXMgYSBiaXQgdG9vIGhpZ2ggdG8gYmUgcmVhbGlzdGljLiBUaGUgY29lZmZpY2llbnQgaXMgYXBwcm94aW1hdGVseSAzLjIsIHdoaWNoIGlzIGdyZWF0ZXIgdGhhbiB0aGUgM3JkIHF1YXJ0aWxlIG9mIHRoZSBkYXRhc2V0LiBUaGlzIGlzIGNhdXNlZCBieSB0aGUgc21hbGwgbnVtYmVyIG9mIGxvbmcgcmVzcG9uc2VzLCB3aGljaCBoYXZlIGEgaHVnZSBpbXBhY3Qgb24gdGhlIHJlZ3Jlc3Npb24uCgpgYGB7cn0KIyBMaW5lYXIgcmVncmVzc2lvbiBvZiBwYWNrZXQgc2l6ZSA8IDE0ODEKbGluZWFyX3JlZyA8LSBsbSh0aW1lIH4gc2l6ZSwgIyByZWdyZXNzaW9uIGZvcm11bGEKICAgICAgICAgICAgICAgIGRhdGE9bG93X3NpemUpICMgZGF0YSBzZXQKc3VtbWFyeShsaW5lYXJfcmVnKTsKYGBgCgpGb3IgdGhlIHNlY29uZCBjbGFzcywgdGhlIGxpbmVhciByZWdyZXNzaW9uIGlzIHRvdGFsbHkgaW5hcHByb3ByaWF0ZSwgdGhlIHNsb3BlLCB0aGUgc3RhbmRhcmQgZXJyb3IgYW5kIFItc3F1YXJlZCBhcmUgYmFkLgpgYGB7cn0KCiMgTGluZWFyIHJlZ3Jlc3Npb24gb2YgcGFja2V0IHNpemUgPj0gMTQ4MQpsaW5lYXJfcmVnIDwtIGxtKHRpbWUgfiBzaXplLCAjIHJlZ3Jlc3Npb24gZm9ybXVsYQogICAgICAgICAgICAgICAgZGF0YT1oaWdoX3NpemUpICMgZGF0YSBzZXQKc3VtbWFyeShsaW5lYXJfcmVnKTsKYGBgCkEgcXVhbnRpbGUgcmVncmVzc2lvbiBzaG91bGQgYmUgbW9yZSBhY2N1cmF0ZS4gVGVzdGluZyBkaWZmZXJlbnQgdGF1cyBjb3VsZCBoZWxwIGRlY2lkaW5nIHdoaWNoIHBlcmNlbnRhZ2Ugb2YgdGltZXMgdG8gdGFrZSAgKGh0dHBzOi8vZGF0YS5saWJyYXJ5LnZpcmdpbmlhLmVkdS9nZXR0aW5nLXN0YXJ0ZWQtd2l0aC1xdWFudGlsZS1yZWdyZXNzaW9uLykKRWFjaCBibGFjayBkb3QgaXMgdGhlIHNsb3BlIGNvZWZmaWNpZW50IGZvciB0aGUgcXVhbnRpbGUgaW5kaWNhdGVkIG9uIHRoZSB4IGF4aXMuIFRoZSByZWQgbGluZXMgYXJlIHRoZSBsZWFzdCBzcXVhcmVzIGVzdGltYXRlIGFuZCBpdHMgY29uZmlkZW5jZSBpbnRlcnZhbC4gVXBlciBxdWFudGlsZSBpcyB3ZWxsIGJleW9uZCB0aGUgbGVhc3Qgc3F1YXJlcy4gQSB0YXUgb2YgMC44IGNvdWxkIGJlIHN1aXRhYmxlLCBhbmQgZW5zdXJlcyB0aGUgcHJldmlvdXMgcmVzdWx0cy4KCmBgYHtyfQpycWZpdCA8LSBycSh0aW1lIH4gc2l6ZSwgZGF0YSA9IGxvd19zaXplLCB0YXU9MTo5LzEwKQpwbG90KHN1bW1hcnkocnFmaXQpLCBwYXJtPSJzaXplIiwgbWFpbj0iU2l6ZSBjb2VmZmljaWVudCBhbG9uZyB3aXRoIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciBlYWNoIHRhdSAoTG93IGNsYXNzKSIpCgpycWZpdCA8LSBycSh0aW1lIH4gc2l6ZSwgZGF0YSA9IGhpZ2hfc2l6ZSwgdGF1PTE6OS8xMCkKcGxvdChzdW1tYXJ5KHJxZml0KSwgcGFybT0ic2l6ZSIsIG1haW49IlNpemUgY29lZmZpY2llbnQgYWxvbmcgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBmb3IgZWFjaCB0YXUgKEhpZ2ggY2xhc3MpIikKYGBgClZpc3VhbGx5LCB0aGlzIHF1YW50aWxlIHJlZ3Jlc3Npb24gc2VlbXMgd2F5IGJldHRlciB0aGFuIHRoZSBsaW5lYXIgb25lLgoKYGBge3J9CnAgPC0gZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fcG9pbnQoYWxwaGE9MC4yKSArCmdlb21fcXVhbnRpbGUocXVhbnRpbGVzID0gMC44LCBkYXRhPWxvd19zaXplLCBjb2xvdXIgPSAiYmx1ZSIsIHNpemUgPSAyKSsKZ2VvbV9xdWFudGlsZShxdWFudGlsZXMgPSAwLjgsIGRhdGE9aGlnaF9zaXplLCBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDIpICsgeWxpbSgwLDEwMCkgKyBnZ3RpdGxlKCJQaW5nIHRpbWUgYWNjb3JkaW5nIHRvIHBhY2tldCBzaXplIHdpdGggcXVhbnRpbGUgcmVncmVzc2lvbnMiKQpwCmBgYAoKVGhlIGV4dHJlbWVseSBsb3cgcC12YWx1ZSBhbmQgc3RhbmRhcmQgZXJyb3Igc2VlbXMgdG8gY29uZmlybSB0aGUgZ3JhcGhpY2FsIHJlc3VsdC4gCmBgYHtyfQojIExvdyBjbGFzcyBxdWFudGlsZSByZWdyZXNzaW9uCnN1bW1hcnkobG0odGltZSB+IHNpemUsIGRhdGE9bG93X3NpemVbbG93X3NpemUkdGltZSA8IHF1YW50aWxlKGxvd19zaXplJHRpbWUsIDAuOCksXSkpOwoKIyBIaWdoIGNsYXNzIHF1YW50aWxlIHJlZ3Jlc3Npb24Kc3VtbWFyeShsbSh0aW1lIH4gc2l6ZSwgZGF0YT1oaWdoX3NpemVbaGlnaF9zaXplJHRpbWUgPCBxdWFudGlsZShoaWdoX3NpemUkdGltZSwgMC44KSxdKSk7CmBgYAoKCkFjY29yZGluZyB0byB0aGUgaW50ZXJjZXB0IGFuZCBzaXplIGVzdGltYXRpb24sIGZvciB0aGUgZmlyc3QgY2xhc3MgOiB0aGUgbGF0ZW5jeSAkTCQgaXMgYXBwcm94aW1hdGVseSBlcXVhbCB0byAkMS4xNDUgKiAxMF4tMyQgc2Vjb25kcywgYW5kIHRoZSBjYXBhY2l0eSAkQyQgdG8gJFxmcmFjezF9ezAuMDAwMjIzNX0gXHNpbWVxIDQ0NzQkIGJ5dGVzL3MuIAoKRm9yIHRoZSBzZWNvbmQgY2xhc3MsICRMIFxzaW1lcSAxLjkgKiAxMF4tMyQgc2Vjb25kcyBhbmQgJEMgXHNpbWVxIFxmcmFjezF9ezAuMDAwMjIzOX0gPSBcc2ltZXEgNDQ2NiQgYnl0ZXMvcy4gVGhlIGNhcGFjaXR5IGRvZXNuJ3QgcmVhbGx5IGNoYW5nZSwgd2hpY2ggaXMgcHJldHR5IGxvZ2ljYWwsIGFuZCB0aGUgbGF0ZW5jeSBxdWl0ZSBpbmNyZWFzZXMgd2l0aCB0aGUgZnJhZ21lbnRhdGlvbiBvZiB0aGUgcGFja2FnZS4KCiMjIFNlY29uZCBkYXRhc2V0CgpUaGUgc2Vjb25kIGRhdGFzZXQgaXMgY2FsbGVkIHN0YWNrb3ZlcmZsb3cuY3N2LCBhbmQgaGFzIGJlZW4gY29udmVydGVkIGZyb20gdGhlIHN0YWNrb3ZlcmZsb3cubG9nLiBJdCByZXN1bHRzIGZyb20gYSBjb25uZWN0aW9uIHRvIGEgcmVtb3RlIHdlYiBzaXRlIGNvbm5lY3Rpb24gd2l0aCBoZWF2eSBsb2Fkcy4KCiMjIyBEYXRhIHZlcmlmaWNhdGlvbgoKVGhlIGZpcnN0IHN0ZXBzIGFyZSBzaW1pbGFyIHRvIHRoZSBmaXJzdCBkYXRhc2V0LiBUaHVzLCB0aGV5IGFyZSBub3QgZGV0YWlsZWQuCmBgYHtyfQpkYXRhIDwtIHJlYWQuY3N2KGZpbGU9Ii4vZGF0YS9zdGFja292ZXJmbG93LmNzdiIsIGhlYWRlcj1UUlVFLCBzZXA9IiAiKTsKCiMgSGVhZGVyCmNvbG5hbWVzKGRhdGEpCgojIEZpcnN0IHJvd3MgCmhlYWQoZGF0YSkKYGBgCmBgYHtyfQpkYXRhJGRhdGUgPSBhbnl0aW1lKGRhdGEkZGF0ZSk7CgojIENoZWNrIGlmIGRhdGUgaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IGNvbnZlcnRlZCBpbiBQT1NJWCBkYXRlcwpjbGFzcyhkYXRhJGRhdGUpOwoKI1NpemUgc2hvdWxkIGJlIGludGVnZXIKY2xhc3MoZGF0YSRzaXplKTsKCiN0aW1lIHNob3VsZCBiZSBhdCBsZWFzdCBudW1lcmljCmNsYXNzKGRhdGEkdGltZSk7CmBgYApgYGB7cn0KbmFfcmVjb3JkcyA9IGFwcGx5KGRhdGEsIDEsIGZ1bmN0aW9uICh4KSBhbnkoaXMubmEoeCkpKQpkYXRhW25hX3JlY29yZHMsXQpgYGAKCiMjIyBBbmFseXNpcwoKVGhhbmtzIHRvIHRoaXMgcGxvdCwgd2UgY2FuIHNlZSB0aGF0IHRoZSBnYXAgYmV0d2VlbiBleHRyZW1lIHZhbHVlcyBpcyBsZXNzIGltcG9ydGFudC4gCkxpa2UgdGhlIGZpcnN0IHNldCwgYSBwcmVkaWN0aWJsZSBwYXR0ZXJuIGNvdWxkIGJlIGEgYml0IHRvdWdoIHRvIGRldGVjdC4KCmBgYHtyfQpwIDwtIHBsb3RfbHkoZGF0YSwgeCA9IH5kYXRlLCBtb2RlPSdsaW5lcycpJT4lCmFkZF90cmFjZSh5ID0gfnRpbWUsIG5hbWUgPSAiZGF0ZSIsIHR5cGU9InNjYXR0ZXIiLCBtb2RlID0gJ2xpbmVzJyk7CnAKYGBgClRoZSBzZXJ2ZXIgYmVpbmcgZmFyIGF3YXksIHRoZSBtaW5pbXVtIHRpbWUgdmFsdWUgZm9yIGEgcGluZyBpcyBiaWdnZXIuCmBgYHtyfQpzdW1tYXJ5KGRhdGFbWyJ0aW1lIl1dKTsKYGBgCgpUaGVyZSBpcyBldmVuIGxlc3MgY29ycmVsYXRpb24gYmV0d2VlbiBzaXplIGFuZCB0aW1lcy4KYGBge3J9CmNvcihzdWJzZXQoZGF0YSwgc2VsZWN0PWMoInNpemUiLCAidGltZSIpKSk7CmBgYAoKTGV0J3MgcGxvdCB0aGUgZGF0YSB0byBoYXZlIGEgbW9yZSBnZW5lcmFsIHBvaW50IG9mIHZpZXcuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fcG9pbnQoYWxwaGE9MC4xKSArIGdndGl0bGUoIlBpbmcgdGltZXMgYWNjb3JkaW5nIHRvIHBhY2tldCBzaXplIikKYGBgCldpdGhvdXQgYW55IHN1cnByaXNlLCB0aGVyZSBpcyB0aGUgc2FtZSBhdmVyYWdlIGRlbGltaXRhdGlvbiBiZXR3ZWVuIHBhY2tldHMgd2hvc2Ugc2l6ZSBpcyBiZWxvdyBvciBncmVhdGVyIHRoYW4gMTQ4MiBieXRlcy4gVGhlIHByb3RvY29sIHVzZWQgaXMgdGhlIHNhbWUsIHdoYXRldmVyIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGNsaWVudHMgYW5kIHRoZSB3ZWIgc2VydmVyLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fcG9pbnQoYWxwaGE9MC4zKSsKY29vcmRfY2FydGVzaWFuKHhsaW09YygxNDUwLDE1MDApKSArIGdndGl0bGUoIlBpbmcgdGltZSB3aXRoIGEgem9vbSBvbiBwYWNrZXQgc2l6ZSBvZiB+IDE0ODAgYnl0ZXMiKQpgYGAKCkxpa2UgdGhlIHNob3J0IGNvbm5lY3Rpb24sIG1vc3QgdGltZXMgYXJlIGdhdGhlcmVkIGF0IGxvd2VyIGJvdW5kcy4gTmV2ZXJ0aGVsZXNzLCB3ZSBjYW4gc2VlIHdpdGggaGlzdG9ncmFtcyB0aGF0IG91dGxpZXJzIGFyZSBtb3JlIGVxdWFsbHkgZGlzdHJpYnV0ZWQgdGhhbiBpbiB0aGUgZmlyc3QgZGF0YXNldC4gSW4gYWRkaXRpb24gdG8gZnJhZ21lbnRhdGlvbiwgd2FpdGluZyB0aW1lIG9uIHRoZSBzZXJ2ZXIgY291bGQgYmUgaW5jcmVhc2VkIGJ5IGhlYXZ5IGxvYWQsIGNyZWF0aW5nIHJlcXVlc3RzIHF1ZXVlcy4gSWYgdGhlIGluaXRpYWwgSUNNUCBwYWNrZXQgaXMgZGl2aWRlZCBpbnRvIG11bHRpcGxlIG9uZXMgKGZyYWdtZW50YXRpb24pLCB0aGVuIHBhY2tldHMgY291bGQgYXJyaXZlIGF0IGRpZmZlcmVudCB0aW1lLCBhbmQgYmUgZmFyIGZyb20gZWFjaCBvdGhlciBpbiB0aGlzIHF1ZXVlLCB3aGljaCBjb3VsZCBleHBsYWluIHRoZSB0aW1lIGRpZmZlcmVuY2UgYmV0d2VlbiBzbWFsbCBhbmQgYmlnIHBhY2tldHMuCgpgYGB7cn0KIyBQYWNrZXRzIHdob3NlIHNpemUgaXMgPCAxNDgyCmxvd19zaXplID0gc3Vic2V0KGRhdGEsIHNpemUgPCAxNDgyKTsKCiMgUGFja2V0cyB3aG9zZSBzaXplIGlzID49IDE0ODIKaGlnaF9zaXplID0gc3Vic2V0KGRhdGEsIHNpemUgPj0gMTQ4Mik7CgpwX2xvdyA8LSBnZ3Bsb3QoZGF0YSA9IGxvd19zaXplLCBtYXBwaW5nID0gYWVzKHggPXRpbWUpKSArCiAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MikgKyBnZ3RpdGxlKCJUaW1lIGZyZXF1ZW5jeSBvZiBwYWNrZXQgXG53aG9zZSBzaXplIDwgMTQ4MiIpCnBfaGlnaCA8LSBnZ3Bsb3QoZGF0YSA9IGhpZ2hfc2l6ZSwgbWFwcGluZyA9IGFlcyh4ID10aW1lKSkgKwogICAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTIpK2dndGl0bGUoIlRpbWUgZnJlcXVlbmN5IG9mIHBhY2tldCBcbndob3NlIHNpemUgPj0gMTQ4MiIpCmdyaWQuYXJyYW5nZShwX2xvdywgcF9oaWdoLCBucm93ID0gMSkKYGBgCgpUaGUgbGluZWFyIHJlZ3Jlc3Npb24gc2VlbXMgYWxzbyBpbmFwcHJvcHJpYXRlLCBpbmNsdWRpbmcgdGhlIHNtYWxsIG51bWJlciBvZiBoaWdoZXIgdGltZXMuIFRoZSBleHBsYW5hdGlvbnMgYXJlIHRoZSBzYW1lIGFzIHRoZSBmaXJzdCBkYXRhc2V0IG9uZXMuCmBgYHtyfQpwIDwtIGdncGxvdChkYXRhID0gZGF0YSwgbWFwcGluZyA9IGFlcyh4ID1zaXplLCB5ID0gdGltZSkpKwpnZW9tX3BvaW50KGFscGhhPTAuMykgKyBnZW9tX3Ntb290aChkYXRhPXN1YnNldChkYXRhLCBzaXplID49IDE0ODApLCBtZXRob2Q9ImxtIiwgc2l6ZT0xLjUsIGFlcyhjb2xvdXI9IlNlY29uZCBjbGFzcyBsaW5lYXIgcmVnIikpKwpnZW9tX3Ntb290aChkYXRhPXN1YnNldChkYXRhLCBzaXplIDwgMTQ4MCksIG1ldGhvZD0ibG0iLCBzaXplPTEuNSwgYWVzKGNvbG91cj0iRmlyc3QgY2xhc3MgbGluZWFyIHJlZyIpKSsgZ2d0aXRsZSgiUGluZyB0aW1lIGFjY29yZGluZyB0byBwYWNrZXQgc2l6ZSB3aXRoIGxpbmVhciByZWdyZXNzaW9ucyIpCnAKYGBgCgpgYGB7cn0KIyBMaW5lYXIgcmVncmVzc2lvbiBvZiBwYWNrZXQgc2l6ZSA8IDE0ODIKbGluZWFyX3JlZyA8LSBsbSh0aW1lIH4gc2l6ZSwgIyByZWdyZXNzaW9uIGZvcm11bGEKICAgICAgICAgICAgICAgIGRhdGE9bG93X3NpemUpICMgZGF0YSBzZXQKc3VtbWFyeShsaW5lYXJfcmVnKTsKYGBgCgoKYGBge3J9CiMgTGluZWFyIHJlZ3Jlc3Npb24gb2YgcGFja2V0IHNpemUgPj0gMTQ4MgpsaW5lYXJfcmVnIDwtIGxtKHRpbWUgfiBzaXplLCAjIHJlZ3Jlc3Npb24gZm9ybXVsYQogICAgICAgICAgICAgICAgZGF0YT1oaWdoX3NpemUpICMgZGF0YSBzZXQKc3VtbWFyeShsaW5lYXJfcmVnKTsKYGBgCkJvdGggdmlzdWFsbHkgYW5kIG51bWVyaWNhbGx5IHdpdGggcC12YWx1ZSBhbmQgci1zcXVhcmVkLCB0aGlzIGxpbmVhciByZWdyZXNzaW9uIHNlZW1zIGluYXBwcm9wcmlhdGUuCgpBIHF1YW50aWxlIHJlZ3Jlc3Npb24gc2hvdWxkIGJlIG1vcmUgYWNjdXJhdGUuIFRoZSByYW5nZSBiZXR3ZWVuIHRoZSAzcmQgcXVhcnRpbGUgYW5kIHRoZSBtaW5pbXVtIHZhbHVlIG9mIHRpbWUgaXMgdGhlIHNhbWUgZm9yIHBhY2tldHMgd2hvc2Ugc2l6ZSBpcyBiZWxvdyAxNDgyLCBhbmQgb25seSBvbmUgZm9yIHRoZSBvdGhlcnMuIFRoZW4sIGEgM3JkIHF1YXJ0aWxlIHJlZ3Jlc3Npb24gc2hvdWxkIGJlIHBlcmZlY3QgdG8gYXBwcm94aW1hdGUgbGF0ZW5jeSBhbmQgY2FwYWNpdHkuCmBgYHtyfQojCnN1bW1hcnkobG93X3NpemVbWyJ0aW1lIl1dKTsKc3VtbWFyeShoaWdoX3NpemVbWyJ0aW1lIl1dKTsKCnAgPC0gZ2dwbG90KGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gYWVzKHggPXNpemUsIHkgPSB0aW1lKSkrCmdlb21fcG9pbnQoYWxwaGE9MC4yKSArIGdlb21fcXVhbnRpbGUocXVhbnRpbGVzID0gMC43NSwgZGF0YT1oaWdoX3NpemUsIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMikgKwpnZW9tX3F1YW50aWxlKHF1YW50aWxlcyA9IDAuNzUsIGRhdGE9bG93X3NpemUsIGNvbG91ciA9ICJibHVlIiwgc2l6ZSA9IDIpICsgZ2d0aXRsZSgiUGluZyB0aW1lIGFjY29yZGluZyB0byBwYWNrZXQgc2l6ZSB3aXRoIHF1YW50aWxlIHJlZ3Jlc3Npb25zIikKcApgYGAKClJlc3VsdHMgc2VlbSBwcmV0dHkgZ29vZC4gVGhlIHNpemUgY29lZmZpY2llbnRzIGluIGJvdGggcXVhbnRpbGVzIHJlZ3Jlc3Npb24gYmVsb3cgZG9uJ3QgYWxsb3cgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMuIEl0IGlzIGhhcmQgdG8gY29tcHV0ZSB0aGUgY2FwYWNpdHkgQyBiZWNhdXNlIG9mIHRoaXMgdW5jZXJ0YWludHkuIFRoZSAoSW50ZXJjZXB0KSBjb2VmZmljaWVudCBoYXMgYW4gZXh0cmVtZWx5IGxvdyBzdGFuZGFyZCBlcnJvciwgYW5kIGlzIGVzdGltYXRlZCBhdCAxMTAgbXMuCgpgYGB7cn0KIyBMb3cgY2xhc3MgM3IgcXVhcnRpbGUgcmVncmVzc2lvbgpzdW1tYXJ5KGxtKHRpbWUgfiBzaXplLCBkYXRhPWxvd19zaXplW2xvd19zaXplJHRpbWUgPCBxdWFudGlsZShsb3dfc2l6ZSR0aW1lLCAwLjc1KSxdKSk7CgojIEhpZ2ggY2xhc3MgM3JkIHF1YXJ0aWxlIHJlZ3Jlc3Npb24Kc3VtbWFyeShsbSh0aW1lIH4gc2l6ZSwgZGF0YT1oaWdoX3NpemVbaGlnaF9zaXplJHRpbWUgPCBxdWFudGlsZShoaWdoX3NpemUkdGltZSwgMC43NSksXSkpOwpgYGAKClRoZSBzZWNvbmQgZGF0YXNldCBzaG93cyBsb25nIGRpc3RhbmNlIGxhdGVuY2llcy4gSW4gY29udHJhcnkgdG8gdGhlIGZpcnN0IGRhdGFzZXQsIHRoZSBsYXRlbmNpZXMgYXJlIHRoZSBzYW1lIGZvciB0aGUgdHdvIGNsYXNzZXMgKH4xMTAgbXMpLCBiZWNhdXNlIHRoZSB0aW1lIGludm9sdmVkIGJ5IHRoZSBkYXRhIHRyYXZlbCBpcyBmYXIgc3VwZXJpb3IgdG8gdGhlIHBhY2tldHMgZnJhZ21lbnRhdGlvbiBjb3N0LiBNb3Jlb3ZlciwgdGhlIGZhY3QgdGhhdCBzaXplIGRvZXNuJ3QgaW5mbHVlbmNlIGF0IGFsbCBwaW5nIHRpbWUgZm9yIHRoZSB0aHJlZSBmaXJzdCBxdWFydGlsZXMgbWFrZXMgdGhlIGFuYWx5c2lzIG9mIHRoZSBjYXBhY2l0eSBub3QgcmVsZXZhbnQuaW4gdGhpcyBzY2VuYXJpby4KCgojIENvbmNsdXNpb24gCgpUaGUgbW9zdCBpbXBvcnRhbnQgcG9pbnQgb2YgdGhpcyBhbmFseXNpcyB0byByZW1lbWJlciwgY291bGQgYmUgdGhhdCBkaXNwbGF5aW5nIG9ubHkgc3VtbWFyaWVzIG9yIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpc24ndCBzdWZmaWNpZW50LiBUaGUgZmlyc3QgZGF0YXNldCdzIGxpbmVhciByZWdyZXNzaW9uIGhhZCBncmVhdCBjb2VmZmljaWVudHMgc3RhdGlzdGljcy4gSXQgaXMgb25seSBhZnRlciAqKmRpc3BsYXlpbmcgdGhlIGRhdGEqKiB0aGF0IGEgc3VpdGFibGUgYW5hbHlzaXMgY291bGQgYmUgZG9uZS4KCkxhdGVuY2llcyBoYXZlIGJlZW4gZXZhbHVhdGVkIGZvciBlYWNoIGRhdGFzZXQuIE5ldmVydGhlbGVzcywgdGhlIGNhcGFjaXR5IGNvdWxkIG5vdCBiZSBldmFsdWF0ZWQgb24gbG9uZyBkaXN0YW5jZSBjb25uZWN0aW9uLiAKCgo=